Imports System.Drawing
Imports System.Drawing.Drawing2D

Namespace Annotations

  ''' <summary>
  ''' A mask is just a bitmap with 1 or 8 bit / pixels that can be drawn over
  ''' another image and contains 'logical' information about the pixels of that underlying image.
  ''' Masks are very fast when it comes to determining if a pixel is part of it or not,
  ''' but they can use up a lot of memory when there's many of them.
  ''' As such they are mainly to be used temporarily, e.g. when performing some 
  ''' image processing task.
  ''' </summary>
  ''' <remarks></remarks>
  Public Class Mask
    Implements ISelectable
#Region "Events"
    ''' <summary>
    ''' Event that is triggered when a property changes.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <remarks></remarks>
    Public Event Changed(ByVal sender As Object, ByVal e As CollectionChangedEventArgs)

    ''' <summary>
    ''' Allows raising this event from derived classes.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Friend Sub OnChangedEvent(ByVal e As CollectionChangedEventArgs)
      RaiseEvent Changed(Me, e)
    End Sub

    Public Event SelectedChanged(ByVal sender As ISelectable) Implements ISelectable.SelectedChanged
    ''' <summary>
    ''' Allows raising this event from derived classes.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <remarks></remarks>
    Protected Friend Sub OnSelectedChangedEvent(ByVal sender As ISelectable)
      RaiseEvent SelectedChanged(sender)
    End Sub
#End Region

#Region "Constructor"

    ''' <summary>
    ''' Create a new mask
    ''' </summary>
    ''' <param name="mask"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal mask As Bitmap)
      Me.new(mask, True)
    End Sub

    ''' <summary>
    ''' Create a new mask
    ''' </summary>
    ''' <param name="mask"></param>
    ''' <param name="enabled"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal mask As Bitmap, ByVal enabled As Boolean)
      Me._bitmap = mask
      Me._enabled = enabled

      'Make sure the bitmap's color map is partly transparent and can be used as a overlay mask
      Me.SetColorMapOpacity(Me.Opacity)
    End Sub

    ''' <summary>
    ''' Create a new mask
    ''' </summary>
    ''' <param name="mask"></param>
    ''' <param name="enabled"></param>
    ''' <param name="visible"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal mask As Bitmap, ByVal enabled As Boolean, ByVal visible As Boolean)
      Me.New(mask, enabled)
      Me._visible = visible
    End Sub
#End Region

#Region "Properties"
    Friend _key As String
    ''' <summary>
    ''' The key by which the Annotation is known in its parent collection.
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    Public ReadOnly Property Key() As String
      Get
        Return _key
      End Get
    End Property

    Dim _selected As Boolean = False
    ''' <summary>
    ''' True if the Annotation is selected.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>Returns false if its parent container is selected!
    ''' </remarks>
    Public Property Selected() As Boolean Implements ISelectable.Selected
      Get
        Return Me._selected
      End Get
      Set(ByVal value As Boolean)
        Me._selected = value
        If value Then
          Me.SetColorMapOpacity(Me.SelectedOpacity)
        Else
          Me.SetColorMapOpacity(Me.Opacity)
        End If
        Me.OnSelectedChangedEvent(Me)
      End Set
    End Property

    Private _bitmap As Bitmap
    ''' <summary>
    ''' The bitmap associated with the mask
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Bitmap() As Bitmap
      'Public ReadOnly Property Bitmap() As Bitmap
      Get
        Return _bitmap
      End Get
      Set(ByVal value As Bitmap)
        Me._bitmap = value
        OnChangedEvent(New CollectionChangedEventArgs(CollectionChangedEventArgs.EventType.PropertySet))
      End Set
    End Property

    Private _enabled As Boolean = True
    ''' <summary>
    ''' Is the mask enabled, i.e. can the user interact with it.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Enabled() As Boolean
      Get
        Return Me._enabled
      End Get
      Set(ByVal value As Boolean)
        Me._enabled = value
        OnChangedEvent(New CollectionChangedEventArgs(CollectionChangedEventArgs.EventType.PropertySet))
      End Set
    End Property

    Private _visible As Boolean = True
    ''' <summary>
    ''' Is the mask visible, i.e. will it be drawn.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Visible() As Boolean
      Get
        Return Me._visible
      End Get
      Set(ByVal value As Boolean)
        Me._visible = value
        OnChangedEvent(New CollectionChangedEventArgs(CollectionChangedEventArgs.EventType.PropertySet))
      End Set
    End Property

    Private _opacity As Integer = 64
    ''' <summary>
    ''' The opacity of a region when not selected
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Opacity() As Integer
      Get
        Return _opacity
      End Get
      Set(ByVal value As Integer)
        _opacity = CInt(Math.Max(Math.Min(255, value), 0))
        OnChangedEvent(New CollectionChangedEventArgs(CollectionChangedEventArgs.EventType.PropertySet))
      End Set
    End Property

    Private _selectedOpacity As Integer = 255
    ''' <summary>
    ''' The opacity of a region when not selected
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property SelectedOpacity() As Integer
      Get
        Return _selectedOpacity
      End Get
      Set(ByVal value As Integer)
        _selectedOpacity = CInt(Math.Max(Math.Min(255, value), 0))
        OnChangedEvent(New CollectionChangedEventArgs(CollectionChangedEventArgs.EventType.PropertySet))
      End Set
    End Property
#End Region

#Region "Methods"

#If IMAGEPROCESSING Then
    ''' <summary>
    ''' Create a mask of a certain size, derived from an annotation. All pixels inside a region
    ''' defined by this annotation will be set to true, others will be false.
    ''' </summary>
    ''' <param name="ann"></param>
    ''' <param name="width"></param>
    ''' <param name="height"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function FromAnnotation(ByVal ann As Annotation, ByVal width As Integer, ByVal height As Integer) As Mask
      Return (FromRegion(ann.GetRegion, width, height, ann.Color))
    End Function

    ''' <summary>
    ''' Create a mask of a certain size, derived from an annotation container. All pixels inside a region
    ''' defined by this annotation container will be set to true, others will be false.
    ''' </summary>
    ''' <param name="annContainer"></param>
    ''' <param name="width"></param>
    ''' <param name="height"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function FromAnnotationContainer(ByVal annContainer As AnnotationContainer, ByVal width As Integer, ByVal height As Integer) As Mask
      Return (FromRegion(annContainer.GetRegion, width, height, annContainer.Color))
    End Function

    ''' <summary>
    ''' Create a mask of a certain size, derived from a region. All pixels inside the region
    ''' will be set to true, others will be false.
    ''' </summary>
    ''' <param name="region"></param>
    ''' <param name="width"></param>
    ''' <param name="height"></param>
    ''' <param name="foreground"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function FromRegion(ByVal region As Region, ByVal width As Integer, ByVal height As Integer, ByVal foreground As Color) As Mask
      Return (New Mask(ImageProcessing.Image1.FromRegion(region, width, height, foreground)))
    End Function
#End If

    ''' <summary>
    ''' Set the opacity of the mask color palette entries. 0 is transparent, 255 is opaque.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub SetColorMapOpacity(ByVal opacity As Integer)
      Dim palette As Drawing.Imaging.ColorPalette = Me._bitmap.Palette
      For i As Integer = 0 To palette.Entries.Length - 1
        If palette.Entries(i).A > 0 Then palette.Entries(i) = Color.FromArgb(opacity, palette.Entries(i))
      Next
      Me._bitmap.Palette = palette
    End Sub

    Friend Sub Draw(ByVal g As Graphics, ByVal srcRect As Rectangle, ByVal dstRect As Rectangle)
      If Me._visible Then
        Debug.WriteLine("Drawing mask of size " & Me.Bitmap.Size.ToString() & " using source rectangle " & srcRect.ToString() & " to destination " & dstRect.ToString())
        g.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
        g.DrawImage(Me._bitmap, dstRect, srcRect, GraphicsUnit.Pixel)
      End If
    End Sub

#End Region

  End Class
End Namespace
